<style lang="less" scoped>
@borderColor: #3ba5e1;
.scroll-table-header {

  & > :first-child {
    border-top-left-radius: 4px;
  }

  & > :last-child {
    border-top-right-radius: 4px;
  }

  & > :nth-child(n) {
    background: #185178;
    border: 1px solid @borderColor;
  }
}

.scroll-table-row, .scroll-table-header {
  display: flex;

  & > :first-child {
    border-left: 1px solid @borderColor;

  }

  & > :nth-child(n) {
    flex: 1;

    overflow-wrap: break-word;

    color: #fff;
    border-bottom: 1px solid @borderColor;
    border-right: 1px solid @borderColor;
    text-align: center;
    background: transparent;
    transition: background 0.3s ease;
  }

  &:hover > :nth-child(n) {
    background: #104b80;
  }

}

.ellipsis-cell {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.table-empty {
  margin: 0;
  border: 1px solid @borderColor;
  border-top: none;
}

.footer {
  margin-top: 10px;
  text-align: right;
  color: white;
}
</style>
<template>
  <div class="page">
    <!--    表头部分的渲染-->
    <div class="scroll-table-header">
      <div v-if="hasSelection" :style="itemStyle(rowSelection)">
        <a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange"/>
      </div>
      <div v-for="item of columns" class="ellipsis-cell" :style="itemStyle(item)">
        <span>{{ item.title }}</span>
      </div>
    </div>
    <!--    表格主体部分的渲染-->
    <RecycleScroller
        v-show="tableData.length>0"
        :key-field="rowKey"
        :style="style"
        :items="tableData"
        :item-size="itemSize"
        :infinite-scroll-disabled="busy"
        :infinite-scroll-distance="infiniteScrollDistance"
        v-infinite-scroll="handleInfiniteOnLoad"
        v-slot="{item:row,index}">
      <a-table></a-table>
      <div class="scroll-table-row">
        <div v-if="hasSelection" :style="itemStyle(rowSelection)">
          <a-checkbox :checked="rowSelection.selectedRowKeys.includes(row[rowKey])"
                      @change="onCheckItem(row)"/>
        </div>
        <div v-for="(key) of columnKeys"
             class="ellipsis-cell"
             :key="key"
             :style="itemStyle(columnMap[key])"
        >
          <slot v-if="izSlotRender(columnMap[key])"
                :name="columnMap[key].scopedSlots.customRender"
                :record="row"
                :text="row[key]">
          </slot>
          <span v-else :title="row[key]">{{ renderItem(row, index, key) }}</span>
        </div>
      </div>
    </RecycleScroller>
    <!--    数据为空时的展示-->
    <a-empty v-show="tableData.length===0" class="table-empty">
      <span slot="description"> 暂无数据！ </span>
    </a-empty>
    <!--    数据展示条数-->
    <div class="footer">
      <span>已加载{{ tableData.length }}条</span>
      <span> - </span>
      <span>共{{ dataSource.length }}条</span>
    </div>
  </div>
</template>
<script>
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
import infiniteScroll from "vue-infinite-scroll";
import {RecycleScroller} from "vue-virtual-scroller";

const columnFieldKey = "dataIndex";
export default {
  directives: {infiniteScroll},
  components: {
    RecycleScroller,
  },
  // 必填参数 columns--列参数、dataSource--不分页的数据源
  props: {
    columns: {
      type: Array,
      required: true,
    },
    dataSource: {
      type: Array,
      required: true,
    },
    itemSize: {
      type: Number,
      required: true,
    },
    // 触发加载的距离条件
    infiniteScrollDistance: {
      type: Number,
      default: 10,
    },
    // 表格展示的默认高度
    height: {
      type: Number,
      default: 500,
    },
    // 表格滚动加载的页条数
    pageSize: {
      type: Number,
      default: 30,
    },
    // rowSelection='{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,width:20}'
    // 表格选择配置--现在只支持多选，没处理单选
    rowSelection: {
      type: Object,
      default: () => null,
    },
    // 表格里的行主键
    rowKey: {
      type: String,
      default: "key",
    },
  },
  computed: {
    // 表格滚动的高度--如果条数过少则使用条数乘以每条高度作为展示高度、否则使用默认的height
    tableHeight() {
      let maxHeight = this.itemSize * this.dataSource.length;
      return maxHeight < this.height ? maxHeight : this.height;
    },
    // 是否展示表格多选
    hasSelection() {
      return this.rowSelection !== null && this.rowSelection.selectedRowKeys && this.rowSelection.onChange;
    },
    // 将列数组转为列对象，将columnFieldKey值作为键，数组项作为值
    columnMap() {
      return this.columns.reduce((returnValue, cur) => {
        returnValue[cur[columnFieldKey]] = cur;
        return returnValue;
      }, {});
    },
    // 取数组里的列键值--columnFieldKey
    columnKeys() {
      return this.columns
          .map(item => item[columnFieldKey]);
    },
    // 表格样式
    style() {
      return {
        height: this.tableHeight + "px",
      };
    },
    // 表格展示的数据，从总数据里面取
    tableData() {
      return this.dataSource.slice(0, this.lastIndex);
    },
    // 展示的最大下标数量，存在比总数据条数多的情况，使用slice解决这个问题
    lastIndex() {
      return this.pageSize * this.page;
    },
    // 选中的表格数据key--排除所有不在dataSource的key
    selectedRowKeys() {
      let allRowKey = this.dataSource.map(item => item[this.rowKey]);
      return this.rowSelection ? this.rowSelection.selectedRowKeys.filter(key => allRowKey.includes(key)) : [];
    },
    // checkbox是否部分选中
    indeterminate() {
      let selectedLength = this.selectedRowKeys.length;
      let allLength = this.dataSource.length;
      return !!selectedLength && selectedLength < allLength;
    },
    // checkbox是否全选
    checkAll() {
      let selectedLength = this.selectedRowKeys.length;
      let allLength = this.dataSource.length;
      return !!selectedLength && selectedLength === allLength;
    },
    busy() {
      return this.lastIndex >= this.dataSource.length;
    },
  },
  data() {
    return {
      // 初始的展示页数
      page: 1,
    };
  },
  watch: {
    // 如果改变了数据源，需要重置
    dataSource: function (val) {
      this.resetData();
    },
  },
  methods: {
    getSelectedRows(selectedRowKeys) {
      return selectedRowKeys
          .map(key => this.dataSource.find(item => item[this.rowKey] === key))
          .filter(item => item !== null);
    },
    callbackParentChange(selectedRowKeys, selectedRows) {
      if (this.hasSelection) {
        this.rowSelection.onChange([...selectedRowKeys], [...selectedRows]);
      }
    },
    resetData() {
      Object.assign(this, {
        // 初始的展示页数
        page: 1,
      });
    },
    // 表格项的样式--设置宽度
    itemStyle(item) {
      return item.width ? {width: item.width + "px", flex: "initial"} : {};
    },
    // 是否使用插槽渲染-- 插槽渲染还存在插槽传参的理解问题--todo
    izSlotRender(item) {
      return item.scopedSlots && item.scopedSlots.customRender;
    },
    // 表格选中单项的回调--如果原来没有就是新增，有就是删除，同时修改部分选中状态和全选状态
    onCheckItem(row) {
      let key = row[this.rowKey];
      let selectedRowKeys = [...this.selectedRowKeys];
      let findIndex = selectedRowKeys.findIndex(item => item === key);
      // 如果有就删除
      if (findIndex !== -1) {
        selectedRowKeys.splice(findIndex, 1);
      } else {
        selectedRowKeys.push(key);
      }
      let selectedRows = this.getSelectedRows(selectedRowKeys);
      this.callbackParentChange(selectedRowKeys, selectedRows);
    },
    // 表头的选择回调
    onCheckAllChange(e) {
      let selectedRows = e.target.checked ? this.dataSource : [];
      let selectedRowKeys = selectedRows.map(item => item[this.rowKey]);
      this.callbackParentChange(selectedRowKeys, selectedRows);
    },
    // 如果column里提供了customRender，则使用提供的函数进行渲染
    renderItem(row, index, key) {
      let customRender = this.columnMap[key].customRender;
      if (customRender) {
        return customRender(row[key], row, index, key);
      } else {
        return row[key];
      }
    },
    // 滑到底部时加载数据
    handleInfiniteOnLoad() {
      if (this.busy) {
        console.log("数据已经加载完毕！");
        return;
      }
      console.log("加载更多数据");
      this.page++;
    },
  },
};
</script>
